package cn.xiuxius.askbox.service.impl;

import cn.hutool.crypto.SecureUtil;
import cn.xiuxius.askbox.common.constant.AccountState;
import cn.xiuxius.askbox.common.constant.RedisConstant;
import cn.xiuxius.askbox.common.exception.*;
import cn.xiuxius.askbox.common.utils.JwtUtil;
import cn.xiuxius.askbox.mapper.UserMapper;
import cn.xiuxius.askbox.pojo.dto.add.UserAddDTO;
import cn.xiuxius.askbox.pojo.dto.common.LoginDTO;
import cn.xiuxius.askbox.pojo.dto.common.RegisterDTO;
import cn.xiuxius.askbox.pojo.dto.query.UserQueryDTO;
import cn.xiuxius.askbox.pojo.dto.update.UserUpdateDTO;
import cn.xiuxius.askbox.pojo.dto.update.UserUpdateStateDTO;
import cn.xiuxius.askbox.pojo.entity.UserEntity;
import cn.xiuxius.askbox.pojo.result.PageResult;
import cn.xiuxius.askbox.pojo.vo.LoginVO;
import cn.xiuxius.askbox.pojo.vo.UserVO;
import cn.xiuxius.askbox.properties.JwtProperties;
import cn.xiuxius.askbox.service.IUserService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;
import java.util.*;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author xiaoxiu
 * @since 2024-07-20
 */
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, UserEntity> implements IUserService {

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private HttpServletRequest request;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private JwtProperties jwtProperties;

    @Override
    public void addUser(UserAddDTO dto) {
        UserEntity userEntity = userMapper.selectOne(Wrappers.<UserEntity>lambdaQuery().
                eq(UserEntity::getUsername, dto.getUsername())
        );

        if (userEntity != null) {
            throw new AccountAlreadyExistsException("用户名已存在");
        }

        userEntity = new UserEntity();
        BeanUtils.copyProperties(dto, userEntity);
        userEntity.setRegisterIp(request.getRemoteAddr());
        userEntity.setLastLoginIp(request.getRemoteAddr());
        userEntity.setUuid(UUID.randomUUID().toString());
        userMapper.insert(userEntity);
    }

    @Override
    public PageResult getUsersByPage(UserQueryDTO dto) {
        LambdaQueryWrapper<UserEntity> wrapper = Wrappers.<UserEntity>lambdaQuery()
                .eq(Objects.nonNull(dto.getState()), UserEntity::getState, dto.getState())
                .like(StringUtils.isNotBlank(dto.getDisplayName()), UserEntity::getDisplayName, dto.getDisplayName())
                .like(StringUtils.isNotBlank(dto.getEmail()), UserEntity::getEmail, dto.getEmail())
                .like(StringUtils.isNotBlank(dto.getPhone()), UserEntity::getPhone, dto.getPhone())
                .like(StringUtils.isNotBlank(dto.getUsername()), UserEntity::getUsername, dto.getUsername())
                .like(StringUtils.isNotBlank(dto.getLastLoginIp()), UserEntity::getLastLoginIp, dto.getLastLoginIp())
                .like(StringUtils.isNotBlank(dto.getRegisterIp()), UserEntity::getRegisterIp, dto.getRegisterIp())
                .between((dto.getCreateTimeBegin() != null && dto.getCreateTimeEnd() != null), UserEntity::getCreateTime, dto.getCreateTimeBegin(), dto.getCreateTimeEnd())
                .orderByDesc(UserEntity::getUpdateTime);
        Page<UserEntity> pageResult = userMapper.selectPage(new Page<>(dto.getPage(), dto.getPageSize()), wrapper);


        List<UserVO> vos = new LinkedList<>();

        for (UserEntity record : pageResult.getRecords()) {
            UserVO vo = new UserVO();
            BeanUtils.copyProperties(record, vo);
            vos.add(vo);
        }
        return new PageResult(pageResult.getTotal(), vos);
    }

    @Override
    public UserVO getUserByUuid(UserQueryDTO dto) {
        UserEntity userEntity = userMapper.selectOne(Wrappers.<UserEntity>lambdaQuery()
                .eq(UserEntity::getUuid, dto.getUuid())
        );
        UserVO vo = new UserVO();
        BeanUtils.copyProperties(userEntity, vo);
        return vo;
    }

    @Override
    public void updateUser(UserUpdateDTO dto) {
        UserEntity userEntity = userMapper.selectOne(Wrappers.<UserEntity>lambdaQuery().
                eq(UserEntity::getUsername, dto.getUsername())
        );

        if (userEntity != null && !userEntity.getUuid().equals(dto.getUuid())) {
            throw new AccountAlreadyExistsException("用户名已存在");
        }

        if (userEntity != null) {
            BeanUtils.copyProperties(dto, userEntity);
        }

        userMapper.update(null, Wrappers.<UserEntity>lambdaUpdate()
                .eq(UserEntity::getUuid, dto.getUuid())
                .set(StringUtils.isNotBlank(dto.getAvatar()), UserEntity::getAvatar, dto.getAvatar())
                .set(StringUtils.isNotBlank(dto.getDisplayName()), UserEntity::getDisplayName, dto.getDisplayName())
                .set(StringUtils.isNotBlank(dto.getEmail()), UserEntity::getEmail, dto.getEmail())
                .set(StringUtils.isNotBlank(dto.getPassword()), UserEntity::getPassword, SecureUtil.sha256(dto.getPassword()))
                .set(StringUtils.isNotBlank(dto.getPhone()), UserEntity::getPhone, dto.getPhone())
                .set(StringUtils.isNotBlank(dto.getUsername()), UserEntity::getUsername, dto.getUsername())

        );
    }

    @Override
    public void updateUserState(UserUpdateStateDTO dto) {
        userMapper.update(null, Wrappers.<UserEntity>lambdaUpdate()
                .eq(UserEntity::getUuid, dto.getUuid())
                .set(Objects.nonNull(dto.getState()), UserEntity::getState, dto.getState())
        );
    }

    @Override
    public void deleteUser(String uuid) {
        userMapper.delete(Wrappers.<UserEntity>lambdaQuery().eq(UserEntity::getUuid, uuid));
    }

    @Override
    public LoginVO login(LoginDTO dto) {
        verifyCaptcha(dto.getCaptchaKey(), dto.getCaptcha());

        UserEntity userEntity = userMapper.selectOne(Wrappers.<UserEntity>lambdaQuery()
                .eq(UserEntity::getUsername, dto.getUsername())
        );

        if (userEntity == null) {
            throw new AccountNotFoundException("用户不存在");
        }

        String password = SecureUtil.sha256(dto.getPassword());

        if (!userEntity.getPassword().equals(password)) {
            throw new LoginFailedException("密码错误");
        }

        if (!userEntity.getState().equals(AccountState.ALIVE)) {
            throw new AccountLockedException("账号状态异常");
        }

        Map<String, Object> claims = new HashMap<>();
        claims.put("uuid", userEntity.getUuid());
        claims.put("username", userEntity.getUsername());

        String token = JwtUtil.createJWT(jwtProperties.getUserSecret(), jwtProperties.getUserExpire(), claims);

        return LoginVO.builder()
                .uuid(userEntity.getUuid())
                .token(token)
                .build();
    }

    @Override
    public void register(RegisterDTO dto) {

        UserEntity userEntity = userMapper.selectOne(Wrappers.<UserEntity>lambdaQuery()
                .eq(UserEntity::getUsername, dto.getUsername())
        );

        if (userEntity != null) {
            throw new AccountAlreadyExistsException("用户名已存在");
        }

        Object result = redisTemplate.opsForValue().get(RedisConstant.EMAIL_CAPTCHA_KEY + dto.getCaptchaKey());

        if (result == null) {
            throw new CaptchaInvalidException("验证码已失效");
        }

        if (!result.equals(dto.getCaptcha())) {
            throw new CaptchaInvalidException("验证码错误");
        }

        String password = SecureUtil.sha256(dto.getPassword());

        dto.setPassword(password);

        userEntity = new UserEntity();

        BeanUtils.copyProperties(dto, userEntity);

        userEntity.setUuid(UUID.randomUUID().toString());

        userEntity.setRegisterIp(request.getRemoteAddr());

        userMapper.insert(userEntity);
    }

    @Override
    public UserVO getUserByUsername(UserQueryDTO dto) {
        UserEntity userEntity = userMapper.selectOne(Wrappers.<UserEntity>lambdaQuery()
                .eq(UserEntity::getUsername, dto.getUsername())
                .eq(Objects.nonNull(dto.getState()), UserEntity::getState, dto.getState())
        );
        UserVO vo = new UserVO();
        BeanUtils.copyProperties(userEntity, vo);
        return vo;
    }


    private void verifyCaptcha(String captchaKey, String captcha) {
        Object result = redisTemplate.opsForValue().get(RedisConstant.LOGIN_CAPTCHA_KEY + captchaKey);
        if (result == null) {
            throw new CaptchaInvalidException("验证码已失效");
        }
        Double resultDouble = (Double) result;
        Double captchaDouble = Double.valueOf(captcha);

        if (!resultDouble.equals(captchaDouble)) {
            throw new CaptchaInvalidException("验证码错误");
        }
        redisTemplate.delete(RedisConstant.LOGIN_CAPTCHA_KEY + captchaKey);
    }
}
